package com.ek.service;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ek.common.Context;
import com.ek.common.task.Discovery;
import com.ek.util.Utils;

/**
 * This thread waits for incoming packets on the socket, when a packet arrives, it's added to
 * {@link TaskManager}, if the packet have same IP address of this machine then discard it.
 * @author G.Santoro
 * @See {@link TaskManager}
 */
public class MulticastReceiver implements Runnable {
    private static final transient Logger log = LoggerFactory.getLogger(MulticastReceiver.class);
    private final Context context;
    private boolean listening;
    private boolean stopped;
    private MulticastSocket multicastSocket;
    private InetAddress group;

    public MulticastReceiver(Context context) {
        // TODO:insert in a settings object
        this(context, "230.0.0.1", 4682);
    }

    public MulticastReceiver(Context context, String groupString, int port) {
        this.context = context;
        this.listening = true;
        this.stopped = false;
        joinGroup(groupString, port);
    }

    public void run() {
        log.debug("running ...");
        while (listening) {
            byte[] buf = new byte[64 * 1024];
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            try {
                multicastSocket.receive(packet);
                if (!Utils.isLocalHost(packet.getAddress())) {
                    log.debug("received multicast packet from " + packet.getAddress().getHostAddress());
                    context.getTaskManager().put(new Discovery(context.getNodeID(), packet.getSocketAddress(), context.getComunicationService(), context.getTaskManager(), context.getRoutingTable()));
                }

            } catch (IOException e) {
                log.debug("run IOException", e);

            }
        }
        stop();
    }

    public void stop() {
        if (!stopped) {
            log.debug("stopping ...");
            listening = false;
            stopped = true;
            try {
                if (multicastSocket != null) {
                    multicastSocket.leaveGroup(group);
                    multicastSocket.close();
                    multicastSocket = null;
                }

            } catch (IOException e) {
                log.error("stop IOException", e);

            }
            log.debug("stopped");
        }
    }

    private void joinGroup(String groupString, int port) {
        log.debug("joinGroup in " + groupString + " and port " + port + "...");
        try {
            multicastSocket = new MulticastSocket(port);
            group = InetAddress.getByName(groupString);
            multicastSocket.joinGroup(group);

        } catch (UnknownHostException e) {
            log.error("joinGroup UnknownHostException", e);

        } catch (IOException e) {
            log.error("joinGroup IOException", e);

        }
        log.debug("joined.");
    }

}